/*
 * 
 * ....................................................................
 * ...######..#####.....##....####..#####..####..#####..######....##...
 * .....##....##..#....###...##.....##.....##....##..#....##.....###...
 * .....##....####....#.##....##....####...####..####.....##....#.##...
 * .....##....##.#...#####.....##...##.....##....##.#.....##...#####...
 * .....##....##..#.#...##..####....##.....####..##..#....##..#...##...
 * ....................................................................
 */

package ua.trasferta.common.collections;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.TreeSet;

import ua.trasferta.common.annotations.createBy;
import static ua.trasferta.common.team.TeamMember.*;
import ua.trasferta.common.utils.Objects;
import static ua.trasferta.common.pattern.Pattern.*;

/**
 * Java class with static utility methods pertaining to {@link Set} instances.
 * 
 * @NOTE this class inspired by <b>google-collections</b> and Joshua Bloch book
 *       for effective programming.
 * @SEE http://code.google.com/p/google-collections/
 * @SEE {@link Lists}.
 * @SEE {@link Maps}.
 * @SEE http://code.google.com/p/trasferta/
 * @author Anton Podviaznikov
 */
@createBy(authors = ANTON_PODVIAZNIKOV)
public final class Sets
{
	/**
	 * Private constructor.
	 */
	private Sets()
	{
		// hidden constructor.
	}

	// HashSet
	/**
	 * If you don't want be confused {@link #FACTORY} pattern should be used.
	 * 
	 * @return a newly created, empty {@code HashSet}.
	 */
	public static <E> HashSet<E> newHashSet()
	{
		return new HashSet<E>();
	}

	/**
	 * If you don't want be confused {@link #FACTORY} pattern should be used.
	 * 
	 * @param elements - the elements that the set should contain.
	 * @return a newly created {@code HashSet} containing those elements (minus
	 *         duplicates).
	 */
	public static <E> HashSet<E> newHashSet(E... elements)
	{
		HashSet<E> set = new HashSet<E>(elements.length);
		Collections.addAll(set, elements);
		return set;
	}

	/**
	 * If you don't want be confused {@link #FACTORY} pattern should be used.
	 * 
	 * @param expectedSize - the expected size.
	 * @return a newly created {@code HashSet}, empty, with enough capacity to
	 *         hold {@code expectedSize} elements without rehashing.
	 */
	public static <E> HashSet<E> newHashSetWithExpectedSize(int expectedSize)
	{
		return new HashSet<E>(expectedSize);
	}

	/**
	 * If you don't want be confused {@link #FACTORY} pattern should be used.
	 * 
	 * @param elements - the elements that the set should contain.
	 * @return a newly created {@code HashSet} containing those elements (minus
	 *         duplicates).
	 */
	public static <E> HashSet<E> newHashSet(Iterable<? extends E> elements)
	{
		if (elements instanceof Collection<?>)
		{
			Collection<? extends E> collection = (Collection<? extends E>)elements;
			return new HashSet<E>(collection);
		}
		else
		{
			return Sets.newHashSet(elements.iterator());
		}
	}

	/**
	 * If you don't want be confused {@link #FACTORY} pattern should be used.
	 * 
	 * @param elements - the elements that the set should contain.
	 * @return a newly created {@code HashSet} containing those elements (minus
	 *         duplicates).
	 */
	public static <E> HashSet<E> newHashSet(Iterator<? extends E> elements)
	{
		HashSet<E> set = Sets.newHashSet();
		while (elements.hasNext())
		{
			set.add(elements.next());
		}
		return set;
	}

	// LinkedHashSet
	/**
	 * If you don't want be confused {@link #FACTORY} pattern should be used.
	 * 
	 * @return a newly created, empty {@code LinkedHashSet}
	 */
	public static <E> LinkedHashSet<E> newLinkedHashSet()
	{
		return new LinkedHashSet<E>();
	}

	/**
	 * If you don't want be confused {@link #FACTORY} pattern should be used.
	 * 
	 * @param elements the elements that the set should contain, in order
	 * @return a newly created {@code LinkedHashSet} containing those elements
	 *         (minus duplicates)
	 */
	public static <E> LinkedHashSet<E> newLinkedHashSet(Iterable<? extends E> elements)
	{
		if (elements instanceof Collection<?>)
		{
			Collection<? extends E> collection = (Collection<? extends E>)elements;
			return new LinkedHashSet<E>(collection);
		}
		else
		{
			LinkedHashSet<E> set = Sets.newLinkedHashSet();
			for (E element : elements)
			{
				set.add(element);
			}
			return set;
		}
	}

	// TreeSet
	/**
	 * If you don't want be confused {@link #FACTORY} pattern should be used.
	 * 
	 * @return a newly created, empty {@code TreeSet}
	 */
	public static <E extends Comparable<?>> TreeSet<E> newTreeSet()
	{
		return new TreeSet<E>();
	}

	/**
	 * If you don't want be confused {@link #FACTORY} pattern should be used.
	 * 
	 * @param elements the elements that the set should contain
	 * @return a newly created {@code TreeSet} containing those elements (minus
	 *         duplicates)
	 */
	public static <E extends Comparable<?>> TreeSet<E> newTreeSet(Iterable<? extends E> elements)
	{
		TreeSet<E> set = Sets.newTreeSet();
		for (E element : elements)
		{
			set.add(element);
		}
		return set;
	}

	/**
	 * If you don't want be confused {@link #FACTORY} pattern should be used.
	 * 
	 * @param comparator the comparator to use to sort the set
	 * @return a newly created, empty {@code TreeSet}
	 * @throws NullPointerException if {@code comparator} is null
	 */
	public static <E> TreeSet<E> newTreeSet(Comparator<? super E> comparator)
	{
		return new TreeSet<E>(Objects.checkNotNull(comparator));
	}
}